﻿using System.Collections.Generic;
using IOP.Extension.Cache;
using IOP.Models.Message.MQTT.Package;
using IOP.Pulsar.MQTT.Abstractions;
using IOP.Models.Message.MQTT;
using IOP.Models.Net;

namespace IOP.Pulsar.MQTT
{
    /// <summary>
    /// 会话中心
    /// </summary>
    public class SessionCenter : ISessionCenter
    {
        /// <summary>
        /// 缓存服务
        /// </summary>
        public readonly ICacheService _Cache;
        /// <summary>
        /// 主题中心
        /// </summary>
        public readonly ITopicCenter _TopicCenter;
        /// <summary>
        /// 锁
        /// </summary>
        private readonly object SyncRoot = new object();
        /// <summary>
        /// 客户端中心
        /// </summary>
        /// <param name="cache">缓存服务</param>
        /// <param name="center">主题中心</param>
        public SessionCenter(ICacheService cache, ITopicCenter center)
        {
            _Cache = cache;
            _TopicCenter = center;
        }

        /// <summary>
        /// 创建会话
        /// </summary>
        /// <param name="socket">当前嵌套字</param>
        /// <param name="package">连接报文</param>
        /// <returns></returns>
        public Session CreateSession(SocketMonitor socket, ConnectPackage package)
        {
            var key = $"{MQTTConstant.SessionPrefix}{package.ClientIdentifier}";
            _Cache.TryGetValue(key, out CacheSession cacheSession);
            if(cacheSession != null)
            {
                if (cacheSession.State == CacheSessionState.IsLogin) throw new MQTTConnectException(package, ReturnCodeType.ClientIdError, $"The client {package.ClientIdentifier} is alreadly login");
                cacheSession.State = CacheSessionState.IsLogin;
                _Cache.Set(key, cacheSession);
                var session = new Session(package.ClientIdentifier, _TopicCenter);
                session.Subscribe(cacheSession.SubscribedTopics);
                return session;
            }
            else
            {
                if (string.IsNullOrEmpty(package.ClientIdentifier))
                    return new Session(socket.Guid.ToString("N"), _TopicCenter, true);
                else
                {
                    var newCacheSession = new CacheSession() { ClientId = package.ClientIdentifier, State = CacheSessionState.IsLogin, SubscribedTopics = new List<CacheSubscribedTopic>() };
                    _Cache.Set(key, newCacheSession);
                    return new Session(package.ClientIdentifier, _TopicCenter);
                }
            }
        }

        /// <summary>
        /// 销毁会话
        /// </summary>
        /// <param name="session"></param>
        public void DisposeSession(Session session)
        {
            if (session != null)
            {
                var key = $"{MQTTConstant.SessionPrefix}{session.ClientId}";
                if (!session.ClearSession)
                {
                    lock (SyncRoot)
                    {
                        _Cache.TryGetValue(key, out CacheSession cacheSession);
                        if (cacheSession != null)
                        {
                            cacheSession.State = CacheSessionState.NotLogin;
                            cacheSession.SubscribedTopics.Clear();
                            foreach (var item in session.Topics)
                            {
                                if(item.Value != null)
                                    cacheSession.SubscribedTopics.Add(new CacheSubscribedTopic() { SubscribedQoS = item.Value.SubscribedQoS, Topic = item.Value.Topic.TopicName });
                            }
                        }
                        else
                        {
                            cacheSession = new CacheSession()
                            {
                                ClientId = session.ClientId,
                                State = CacheSessionState.NotLogin,
                                SubscribedTopics = new List<CacheSubscribedTopic>()
                            };

                        }
                        _Cache.Set(key, cacheSession);
                    }
                }
                else
                {
                    _Cache.Remove(key);
                }
                session.Dispose();
            }
        }
    }
}
